"""Common code to both triggers and aliases.

This includes a base class, and utilities for constructing them, especially
using decorators around functions.
"""
import re
import traceback


class Matcher(object):

    def __init__(self,regex=True,regexExpr='',wild=None,func=None):

        if regex:
            self._match=lambda x: list(re.compile(regexExpr).finditer(x))

        else:
            self._match=wild

        if func:
            self.func=func

    def match(self,line):
        return self._match(line)

    def __call__(self,matching,realm):
        return self.func(matching,realm)

def match(param):

    regexExpr=''
    func=None

    if isinstance(param,str):
        regexExpr=param
        regex=True

    elif callable(param):
        func=param
        regex=False

    def func(f):
        return Matcher(regex=regex,regexExpr=regexExpr,func=f)

    return func

class BaseMatchingRealm(object):

    """A realm representing the matching of triggers or aliases."""

    def __init__(self, root, parent, send_line_to_mud):
        self.root = root
        self.parent = parent
        self._writing_after = []
        self.send_line_to_mud = send_line_to_mud

    def _write_after(self):
        """Write everything we've been waiting to."""
        for noteline, sls in self._writing_after:
            self.parent.write(noteline, sls)

    def write(self, line, soft_line_start = False):
        """Write a line to the screen.

        This buffers until the original line has been displayed or echoed.
        """
        self._writing_after.append((line, soft_line_start))

    def send(self, line, echo = False):
        """Send a line to the MUD immediately."""
        self.parent.send(line, echo)

    def _match_generic(self, line, matchers):
        """Test each matcher against the given line, and run the functions
        of those that match.

        This is suitable for use with either triggers or aliases, because of
        the commonality of their APIs.
        """
        for matcher in matchers:
            matches = matcher.match(line)
            for match in matches:
                matcher(match, self)

    def trace(self, line):
        """Forward the debugging decision to our parent."""
        self.parent._trace_with(line, self)

    def trace_thunk(self, thunk):
        self.parent._trace_thunk_with(thunk, self)

    def _trace_with(self, line, realm):
        """Forward a trace request up the stack of realms."""
        self.parent._trace_with(line, realm)

    def _trace_thunk_with(self, thunk, realm):
        self.parent._trace_thunk_with(thunk, realm)
